package com.casit.base.rest;


import com.casit.base.config.CasitBaseProperties;
import com.casit.base.entity.BaseEntity;
import com.casit.base.exception.ResultException;
import com.casit.base.model.ResultModel;
import com.casit.base.service.BaseService;
import com.casit.base.util.ResultUtil;
import com.monitorjbl.json.JsonResult;
import com.monitorjbl.json.JsonView;
import com.monitorjbl.json.Match;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import java.beans.PropertyEditorSupport;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;


public class BaseApi<Entity,ID extends Serializable> {
    protected final static Logger logger = LoggerFactory.getLogger(BaseApi.class);
    protected BaseService baseService;
    protected String[] filterResult= new String[]{"xDeleteDate","xSend", "xVersion", "xCreateBy", "xUpdateBy","xDeleteBy","xFlag"};
    protected Class<Entity> entityClass;
    @Autowired
    private CasitBaseProperties casitBaseProperties;

    /**
     * JSON-view对象
     */
    protected JsonResult jsonResult = JsonResult.instance();
    public BaseApi( BaseService baseService){
        this.baseService= baseService;
    }
    @GetMapping("/{id}")
    public ResultModel get(@PathVariable("id") ID id)
    {
        return  ResultUtil.success(baseService.get(id));
    }
    /**
     * 此方法需要拥有最大权限，不返回分页信息
     * 不带条件的分布查询
     * @param pageable ,默认URL格式为?size=5&page=1
     *                 page，第几页，从0开始，默认为第0页      size，每一页的大小，默认为20
     * sort，排序相关的信息，以property,property(,ASC|DESC)的方式组织，例如sort=firstname&sort=lastname,desc表示在按firstname正序排列基础上按lastname倒序排列。
     * search=name:eq:billow36[:and | or];phone:like:130[:and | or];( state:gt:5[:and | or];flag:eq:5[:and | or])
     * @return
     */
    @GetMapping("/simple")
    public ResultModel getSimpleList(@PageableDefault(value = 15, sort = { "xInsertDate" },
            direction = Sort.Direction.DESC)
                                       Pageable pageable,
                               @RequestParam(value = "search", required = false, defaultValue = "") String search,
                               @RequestParam(value = "disableFilter", required = false, defaultValue = "") String disableFilter,
                               @RequestParam(value = "filterExclude", required = false, defaultValue = "1") String filterExclude,
                               @RequestParam(value = "filterInclude", required = false, defaultValue = "1") String filterInclude,
                               @RequestParam(value = "self", required = false, defaultValue = "false") String self){

        return this.getList(pageable,search,disableFilter,filterExclude,filterInclude,self=="true",true,true);
    }
    /**
     * 此方法需要拥有最大权限，后续会添加上
     * 不带条件的分布查询
     * @param pageable ,默认URL格式为?size=5&page=1
     *                 page，第几页，从0开始，默认为第0页      size，每一页的大小，默认为20
     * sort，排序相关的信息，以property,property(,ASC|DESC)的方式组织，例如sort=firstname&sort=lastname,desc表示在按firstname正序排列基础上按lastname倒序排列。
     * search=name:eq:billow36[:and | or];phone:like:130[:and | or];( state:gt:5[:and | or];flag:eq:5[:and | or])
     * @return
     */
    @GetMapping
    public ResultModel getList(@PageableDefault(value = 15, sort = { "xInsertDate" },
            direction = Sort.Direction.DESC)
                                       Pageable pageable,
                               @RequestParam(value = "search", required = false, defaultValue = "") String search,
                               @RequestParam(value = "disableFilter", required = false, defaultValue = "") String disableFilter,
                               @RequestParam(value = "filterExclude", required = false, defaultValue = "1") String filterExclude,
                               @RequestParam(value = "filterInclude", required = false, defaultValue = "1") String filterInclude,
                               @RequestParam(value = "self", required = false, defaultValue = "false") String self){

        return this.getList(pageable,search,disableFilter,filterExclude,filterInclude,self=="true",true,false);
    }
    /**
     * 不带条件的分布查询
     * @param pageable ,默认URL格式为?size=5&page=1
     *                 page，第几页，从0开始，默认为第0页      size，每一页的大小，默认为20
     * sort，排序相关的信息，以property,property(,ASC|DESC)的方式组织，例如sort=firstname&sort=lastname,desc表示在按firstname正序排列基础上按lastname倒序排列。
     * search=name:eq:billow36[:and | or];phone:like:130[:and | or];( state:gt:5[:and | or];flag:eq:5[:and | or])
     * @return
     */
    @GetMapping("/all")
    public ResultModel getAllList(@PageableDefault(value = 15, sort = { "xInsertDate" },
            direction = Sort.Direction.DESC)
                                       Pageable pageable,
                               @RequestParam(value = "search", required = false, defaultValue = "") String search,
                               @RequestParam(value = "disableFilter", required = false, defaultValue = "") String disableFilter,
                               @RequestParam(value = "filterExclude", required = false, defaultValue = "1") String filterExclude,
                               @RequestParam(value = "filterInclude", required = false, defaultValue = "1") String filterInclude){
        return this.getList(pageable,search,disableFilter,filterExclude,filterInclude,false,false,false);
    }

    /**
     * 不带条件的分布查询
     * @param pageable ,默认URL格式为?size=5&page=1
     *                 page，第几页，从0开始，默认为第0页      size，每一页的大小，默认为20
     * sort，排序相关的信息，以property,property(,ASC|DESC)的方式组织，例如sort=firstname&sort=lastname,desc表示在按firstname正序排列基础上按lastname倒序排列。
     * search=name:eq:billow36[:and | or];phone:like:130[:and | or];( state:gt:5[:and | or];flag:eq:5[:and | or])
     * @return
     */

    protected ResultModel getList(Pageable pageable,
                               String search,
                               String disableFilter,
                               String filterExclude,
                               String filterInclude,Boolean self, Boolean valid,boolean simple){



        if(pageable.getPageSize()> casitBaseProperties.getMaxPageSize()){
            throw new ResultException(-1,"一次性查询数据过多，每次最多查询:"+ casitBaseProperties.getMaxPageSize()+"条记录");
        }
        String[] filterExcludeArray = new String[]{"1"};
        if(!StringUtils.isEmpty(filterExclude)&&"1".equals(filterExclude)){
            //添加额外的过滤项
            filterExcludeArray = filterExclude.split(",");
        }
        if(!StringUtils.isEmpty(disableFilter)&&"true".equals(disableFilter)){
            if(simple)
                return ResultUtil.success( baseService.getList(pageable,search,self,valid).getContent());
            return ResultUtil.success(baseService.getList(pageable,search,self,valid));
        }
        String[] filterIncludeArray = new String[]{"1"};
        if(!StringUtils.isEmpty(filterInclude)&&"1".equals(filterInclude)){
            //添加额外的过滤项
            filterIncludeArray = filterInclude.split(",");
        }
        if(simple)
            return jsonResult.use(JsonView.with(ResultUtil.success(baseService.getList(pageable,search,self,valid).getContent()))
                    .onClass(BaseEntity.class, Match.match()
                            .exclude(filterResult)
                            .exclude(filterExcludeArray)
                            .include(filterIncludeArray)
                    )
            )
                    .returnValue();
        return jsonResult.use(JsonView.with(ResultUtil.success( baseService.getList(pageable,search,self,valid)))
                .onClass(BaseEntity.class, Match.match()
                        .exclude(filterResult)
                        .exclude(filterExcludeArray)
                        .include(filterIncludeArray))
        )
                .returnValue();

    }
    @PostMapping
    public ResultModel save(@Validated @RequestBody Entity entity, BindingResult bindingResult){
        if (bindingResult.hasErrors()){
            List<ObjectError> errorList = bindingResult.getAllErrors();
            String errorMessage = "";
            for(ObjectError error : errorList){
                errorMessage += error.getDefaultMessage()+",";
            }
            return ResultUtil.fail(errorMessage.substring(0,errorMessage.length()-1));
        }
        return jsonResult.use(JsonView.with(ResultUtil.success(baseService.save(entity)))
                .onClass(BaseEntity.class, Match.match().exclude(filterResult))).returnValue();
    }
    @PostMapping("/remove/{id}")
    public ResultModel remove( @PathVariable("id")  ID id){
        baseService.delete(id);
        return ResultUtil.success("");
    }
    @PutMapping("/{id}")
    public ResultModel update(
            @PathVariable("id")  ID id,
            @RequestBody Entity entity){
        return jsonResult.use(JsonView.with(ResultUtil.success(baseService.update(id,entity)))
                .onClass(BaseEntity.class, Match.match().exclude(filterResult))).returnValue();
    }
    @DeleteMapping("/{id}")
    public ResultModel delete(
            @PathVariable("id")  ID id){
        baseService.softDelete(id);
        return ResultUtil.success("");
    }

    public Class<Entity> getEntityClass() {
        if(entityClass==null){
            entityClass=  (Class<Entity>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
        return entityClass;

    }

    public void setEntityClass(Class<Entity> entityClass) {
        this.entityClass = entityClass;
    }

    /**
     * 用于更新操作里，日期为NULL是处理
     * @param dataBinder
     */
    @InitBinder
    public void InitBinder(WebDataBinder dataBinder)
    {
        dataBinder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
            public void setAsText(String value) {
                try {
                    setValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value));
                } catch(ParseException e) {
                    setValue(null);
                }
            }

            public String getAsText() {
                return new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss").format((Date) getValue());
            }

        });

    }
}
